<?php
namespace App\Logging;

use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\LogRecord;

class SizeRotatingFileHandler extends StreamHandler
{
    protected int $maxFileSize;
    protected int $maxFiles;
    protected string $filename;

    public function __construct(
        string $filename,
        int $maxFileSize,
        int $maxFiles = 0,
        $level = Level::Debug,
        bool $bubble = true,
        ?int $filePermission = null,
        bool $useLocking = false
    ) {
        $this->filename = $filename;
        $this->maxFileSize = $maxFileSize;
        $this->maxFiles = $maxFiles;

        parent::__construct($filename, $level, $bubble, $filePermission, $useLocking);
    }

    protected function write(LogRecord $record): void
    {
        // 原子化文件大小检查与写入
        $this->performSizeCheck();
        parent::write($record);
    }

    protected function performSizeCheck(): void
    {
        // 使用文件锁确保原子操作
        $handle = fopen($this->filename, 'a');
        if (!$handle) {
            return;
        }

        try {
            if ($this->useLocking) {
                flock($handle, LOCK_EX);
            }

            clearstatcache(true, $this->filename);
            if (file_exists($this->filename) &&
                filesize($this->filename) >= $this->maxFileSize) {
                $this->rotate();
            }
        } finally {
            if ($this->useLocking) {
                flock($handle, LOCK_UN);
            }
            fclose($handle);
        }
    }

    protected function rotate(): void
    {
        // 关闭当前文件流（如果存在）
        if (is_resource($this->stream)) {
            fclose($this->stream);
            $this->stream = null;
        }

        // 生成带时间戳的新文件名
        $fileInfo = pathinfo($this->filename);
        $newFilename = sprintf(
            '%s/%s-%s%s',
            $fileInfo['dirname'],
            $fileInfo['filename'],
            date('Ymd-His'),
            isset($fileInfo['extension']) ? '.'.$fileInfo['extension'] : ''
        );

        // 原子性轮转操作
        if (file_exists($this->filename)) {
            rename($this->filename, $newFilename);
        }

        // 清理旧文件
        $this->purgeOldFiles();

        // 重新初始化文件流
        $this->stream = fopen($this->filename, 'a');
        if ($this->filePermission) {
            chmod($this->filename, $this->filePermission);
        }
    }

    protected function purgeOldFiles(): void
    {
        if ($this->maxFiles <= 0) {
            return;
        }

        $fileInfo = pathinfo($this->filename);
        $pattern = sprintf(
            '%s/%s-*%s',
            $fileInfo['dirname'],
            $fileInfo['filename'],
            isset($fileInfo['extension']) ? '.'.$fileInfo['extension'] : ''
        );

        $files = @glob($pattern);
        if (!is_array($files)) {
            return;
        }

        // 按修改时间排序
        usort($files, function ($a, $b) {
            return filemtime($b) <=> filemtime($a);
        });

        // 保留指定数量的文件
        foreach (array_slice($files, $this->maxFiles) as $file) {
            @unlink($file);
        }
    }
}
